Skip to content

Triggered Lightning: Implement Layer Mean Temperature Plugins#2321

Merged
Anzerkhan27 merged 24 commits intomasterfrom
EPPT-2747-triggered-lightning-layer-mean-temperature-plugin
Mar 12, 2026
Merged

Triggered Lightning: Implement Layer Mean Temperature Plugins#2321
Anzerkhan27 merged 24 commits intomasterfrom
EPPT-2747-triggered-lightning-layer-mean-temperature-plugin

Conversation

@Anzerkhan27
Copy link
Contributor

@Anzerkhan27 Anzerkhan27 commented Mar 3, 2026

EPPT-3150

This PR implements the mean layer temperature calculation step as part of the ongoing migration of the Helicopter-Triggered Lightning (GPP → EPP) workflow (eppuk_triggered_lightning).

The GPP implementation contained a single monolithic function calculate_layer_mean_temperature() which handled both the vertical layer extraction and the weighted mean calculation in one function. This PR introduces two IMPROVER-style plugins to replace this, following EPP conventions.

Two new plugins have been implemented:

1. LayerExtractionAndInterpolation

  • Extracts all UKV temperature model levels falling strictly between 2000 ft and 3000 ft (converted to metres).
  • Interpolates temperature at the exact 2000 ft and 3000 ft boundaries using iris.analysis.Linear().
  • Concatenates the interpolated boundaries with the extracted interior levels into a single cube.

2. CalculateLayerMeanTemperature

  • Takes the output of LayerExtractionAndInterpolation as input.
  • Computes the altitude-weighted mean temperature across the layer using a trapezoidal weighting scheme (thickness between levels as weights).
  • Returns a 2D Iris cube (projection_y_coordinate, projection_x_coordinate) with scalar coordinates forecast_period, forecast_reference_time, and time preserved from the input.
  • Output metadata uses standard_name="air_temperature" (CF convention) with units="K".

@Anzerkhan27 Anzerkhan27 changed the title Add layer extraction and interpolation plugin Add Improver plugins to calculate Layer Mean temperature for Triggered Lightning Mar 3, 2026
@Anzerkhan27 Anzerkhan27 changed the title Add Improver plugins to calculate Layer Mean temperature for Triggered Lightning Triggered Lightning: Implement Layer Mean Temperature Plugins Mar 5, 2026
Copy link
Contributor

@robertplatt-mo robertplatt-mo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Anzerkhan27 I've requested a few changes the main comments being in layer_mean_temperature and probably the most time consuming being type hints which I understand improver style guide expects.

Generally it all looks pretty sensible though.

(base, interior, and top).
"""
if bottom >= top:
raise ValueError(f"bottom ({bottom} ft) must be less than top ({top} ft).")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise ValueError(f"bottom ({bottom} ft) must be less than top ({top} ft).")
raise ValueError(f"Bottom ({bottom} ft) must be less than top ({top} ft).")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change added

from improver import BasePlugin


class LayerExtractionAndInterpolation(BasePlugin):
Copy link
Contributor

@robertplatt-mo robertplatt-mo Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not keen on the name of this plugin. Firstly extract and interpolate feels like it does two things. Generally it's good if functionality can be explained as doing one thing. Extraction seems to me to be something we do in service of the more important interpolation. The name also doesn't communicate that the method is primarily a tool for temperature interpolation at layer boundaries. This makes the tool seem like it's generic when it surely is not.

How about BoundaryTemperatureInterpolation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or LayerTemperatureInterpolation or TemperatureInterpolationAtLayers?

Copy link
Contributor Author

@Anzerkhan27 Anzerkhan27 Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are correct, the main overarching thing the function does here is Interpolate temperature at given heights. The extraction depends upon the boundary arguments that we provide. So it make sense to rename the plugin to LayerTemperatureInterpolation

Extract and interpolate temperature values at layer boundaries.

Args:
temp_cube (iris.cube.Cube): Input temperature cube with a height coordinate.
Copy link
Contributor

@robertplatt-mo robertplatt-mo Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ryan has previously informed me that the way improver docs are generated we don't need type hints in the args as these can be dynamically generated for the types (for more info on this though probably best to ask him).

I noticed this and then subsequently noticed we haven't got any type hints. My understanding is the improver style guide has these as a requirement. Please consider this comment to apply to all docstrings and all methods (sorry I know it's a pain).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type hints added to method signature

iris.Constraint(
height=lambda point: bottom / self.metres_to_ft
< point
< top / self.metres_to_ft
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted above I don't think we should be passing this in (but please correct me if I'm mistaken and there is a good reason I am not aware of). I'd set it at the top of the file as a constant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have change it to a constant added at the top of file

if verbosity:
print("Layer mean temperature array:", lmt_array)

# Wrap result in a cube (add metadata as needed)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Wrap result in a cube (add metadata as needed)
# Wrap result in a cube and add required metadata

As needed rather implies the user will choose what metadata is added imv

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you pointing this out. I have made the change

@Anzerkhan27
Copy link
Contributor Author

Anzerkhan27 commented Mar 9, 2026

@Anzerkhan27 I've requested a few changes the main comments being in layer_mean_temperature and probably the most time consuming being type hints which I understand improver style guide expects.

Generally it all looks pretty sensible though.

Thank you @robertplatt-mo . I have added the METRES_TO_FT as a constant at top of the file rather than as an argument, changed the plugin name to reflect its main function. I have also added type hints in the method signature and removed it from the docstrings. Also added the small changes to the comments and Docstrings

"""
height_points = np.array([100, 200, 300], dtype=np.float32)
y_points = np.array([0, 1], dtype=np.float32)
x_points = np.array([0, 1], dtype=np.float32)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If lines 22-24 were customised to the shape of the input data, then
make_layer_cube would be more general. I think make_layer_cube
could be used in more places in the unit tests.

Copy link
Contributor

@mo-DavidJohnJohnston mo-DavidJohnJohnston left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an extra test after discussions with Anzer to check the mean layer temperature
when there is a varying temperature field. The existing check of the output value is for a
constant value fields.

Copy link
Contributor

@mo-DavidJohnJohnston mo-DavidJohnJohnston left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes discussed with Anzer have been made.

One extra unit test was needed to cover the case
when the temperature field is not constant.

I approve the PR.


from improver import BasePlugin

METRES_TO_FT = 3.28084
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a comment, to avoid this hardcoded conversion factor, you could use the cf_units package i.e. https://cf-units.readthedocs.io/en/latest/unit.html#cf_units.Unit.convert to handle the unit conversion, which we use elsewhere in IMPROVER.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the comment @gavinevans. I added cf_units to handle unit conversion, it looks cleaner.

@Anzerkhan27
Copy link
Contributor Author

Changes discussed with Anzer have been made.

One extra unit test was needed to cover the case when the temperature field is not constant.

I approve the PR.

Thank your adding in the missing test and fixing docstrings

Copy link
Contributor

@mo-DavidJohnJohnston mo-DavidJohnJohnston left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest change to use cf_units for conversion is an improvement.
I have not checked the unit tests this time, but I am assuming this is a
formality, and I approve the PR.

@Anzerkhan27 Anzerkhan27 merged commit 9a6e346 into master Mar 12, 2026
7 checks passed
@gavinevans gavinevans deleted the EPPT-2747-triggered-lightning-layer-mean-temperature-plugin branch March 13, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants